#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun May 20 19:45:40 2018
@project: 天池比赛-A股主板上市公司公告信息抽取
@group: MZH_314
@author: LHQ
"""
import re
from collections import deque
from collections import namedtuple

import pandas as pd
from parsel import Selector

from .table import Table, ZjcTable
from ..utils import editdistance



Paragraph = namedtuple("paragraph", "title text")


class Report:
    """
    公告类，提供title,first_page等属性
    
    usage:
    ----------
    >> report = Report(text=htmltext, report_id=report_id)
    >> title = report.title
    >> first_page = report.first_page
    ...
    """
    def __init__(self, html, report_id=None):
        self.selector = Selector(text=html)
        self.report_id = report_id
        
    @property
    def title(self):
        """公告标题
        """
        title = self.selector.xpath("//div[@title and @type='pdf']/@title").extract_first()
        return title
    
    @property
    def full_title(self):
        """完整公告标题
        """
        simple_title = re.sub("\s", '',self.title)
        text = self.selector.xpath("//div[@id='SectionCode_1']//text()").extract()
        text = [re.sub("\s", "", s) for s in text if s.strip() and '代码' not in re.sub("\s", "", s)]
        tmp = []
        s = ''
        for i, s_i in enumerate(text):
            if '本公司' in s_i:
                s = ''
                continue
            s += s_i
            score = editdistance(simple_title, s_i)
            if len(s) < 4 * len(simple_title):
                tmp.append((score, s))
        try:
            tmp = [tp for tp in tmp if len(tp[1]) > 10]
            title = min(tmp)[1]
        except ValueError:
            return simple_title
        
        if len(title) > len(simple_title):
            return title
        else:
            return simple_title
        
    @property
    def company_shortname(self):
        """公司简称
        """
        text = self.selector.xpath("//div[@id='SectionCode_1']//text()").extract()
        for txt in text:
            m = re.search("(?:证\s*券|股\s*票|A\s*股)\s*简\s*称[：\s:]+(\S+)?(?:公告|证券|股票)?", txt)
            if m:
                name = m.group(1)
                break
        else:
            # 可能是新上市公司
            name = 'notfound'
        return name

    @property
    def company_fullname(self):
        full_title = self.full_title
        company = re.findall("^.+?有限公司", full_title)
        if len(company) != 0:
            return company[0]
        else:
            return None
    
    @property
    def first_page(self):
        """公告第一页内容
        """
        content = self.selector.xpath("//div[@id='SectionCode_1']//text()").extract()
        return [s.strip() for s in content if s.strip()]
    
    @property
    def contents(self):
        """公告的文本内容，是个字符串数组L
        """
        content = []
        for sec in self.selector.xpath("//div[contains(@id, 'SectionCode')]"):
            item = sec.xpath(".//text()").extract()
            item = [s.strip() for s in item if s.strip()]
            content.append(item)
        return content
    
    @property
    def flat_contents(self):
        """一维的公告文本内容
        """
        contents = []
        for p_sel in self.selector.xpath("//div[@type='paragraph']"):
            p_title = p_sel.xpath("@title").extract()
            content = [re.sub("\s", "", s) for s in p_sel.xpath("./div[@type='content']/text()").extract() if s.strip()]
            contents.extend(p_title)
            contents.extend(content)
        return contents

    @property
    def flat_contents2(self):
        """一维的公告文本内容
        """
        contents = []
        for p_sel in self.selector.xpath("//div[@type='paragraph']"):
            p_title = p_sel.xpath("@title").extract()
            content = "".join(p_sel.xpath("./div[@type='content']/text()").extract())
            contents.extend(p_title)
            contents.append(re.sub("\s", "", content))
        return contents    

    @property
    def raw_tables(self):
        """文本里出现的表格数据，格式为数组，每个元素是一个二元元组（表格元素，表格内容）
        """
        tables = []
        q_j = deque()
        for tb_sel in self.selector.xpath("//table"):
            tb = []
            col_num = 0
            for sel in tb_sel.xpath("tbody/tr[1]/td"):
                num = int(sel.xpath("@colspan").extract_first('1'))
                col_num += num
            col_cache = [[i, 0, None] for i in range(col_num)]
            for row_sel in tb_sel.xpath("tbody/tr"):
                inds = [ele[0] for ele in col_cache if ele[1] == 0]
                row = []
                for ele in col_cache:
                    if ele[1] > 0:
                        ele[1] -= 1
                        row.append((ele[0], ele[2]))
                    else:
                        ele[2] = None
                
                q_j.clear()
                q_col = []
                for j in range(len(inds)):
                    q_j.append(j)                    
                    i = inds[j]
                    if len(q_col) > 0:
                        (value, rowspan) = q_col.pop()
                        row.append((i, value))
                        if rowspan > 0:
                            col_cache[i] = [i, rowspan-1, value]
                        continue
                    else:
                        jj = q_j.popleft()
                        try:
                            col_sel = row_sel.xpath("td")[jj]
                        except IndexError:
                            # 定增20371858.html出现最后一行的最后一列为空时，省略td
                            value = ""
                        else:
                            value =  "".join(col_sel.xpath(".//text()").extract())
                            value = re.sub("\s", '', value)
                        row.append((i, value))
                        rowspan = int(col_sel.xpath("@rowspan").extract_first('0'))
                        if rowspan > 0:
                            col_cache[i] = [i, rowspan-1, value]  # 因为这一次消耗了一次                        
                        colspan = int(col_sel.xpath("@colspan").extract_first('1'))
                        for _ in range(colspan-1):
                            q_col.append((value, rowspan))
                tb.append([u[1] for u in sorted(row, key=lambda x: x[0])])
#            title = tb_sel.xpath("./../../@title").extract_first('')
            title = tb_sel.xpath("./ancestor::div[@type='paragraph']/@title").extract()
            for i in range(4):
                xpath = "./../preceding-sibling::div[last()-%d]//text()" % i
                desc = "".join([s.strip() for s in tb_sel.xpath(xpath).extract()])
                if bool(desc):
                    break
            else:
                desc = ""
            desc = "\t".join(title + [desc])
            tables.append((desc,tb))
        return tables
    
    @property
    def tables(self):
        """Table封装后的表格
        """
        tables = [Table(desc, rows) for desc, rows in self.raw_tables]
        return tables
    
    def has_table(self):
        tb = self.selector.xpath("//table")
        if len(tb) > 0:
            return True
        else:
            return False
                    

class ZjcReport(Report):
    """
    增减持公告
    """
    @property
    def tables(self):
        """Table封装后的表格
        """
        tbs = [ZjcTable(*data) for data in self.raw_tables]
        return tbs


    def getfullname_by_shortname(self, shortname):
        """由股东简称获取股东全称
        """
#        fullname = self.selector.xpath("//text()").re_first("股东“?([^”]*?)”?\s*（以下简称\s*“%s”）"%shortname)
        fullname = self.selector.xpath("//text()").re_first("股东“?([^”，。》、]*?)”?\s*[^）]{,2}(?:以下简称|下称)\s*“?%s”?"%shortname)
        if fullname is None:
            fullname = self.selector.xpath("//text()").re_first("、\s*“?([^”，。》、]*?)”?\s*[^），]{,2}(?:以下简称|下称)\s*“%s”"%shortname)
        if fullname is None:
            fullname = self.selector.xpath("//text()").re_first("收到\s*“?([^”，。》、]*?)”?\s*[^），]{,2}(?:以下简称|下称)\s*“%s”"%shortname)
        if fullname is None:
            fullname = self.selector.xpath("//text()").re_first("公司“?([^”，，。》、]*?)”?\s*[^）]{,2}(?:以下简称|下称)\s*“?%s”?"%shortname)
#            print("####", fullname)
        return fullname
    
    
    def get_shortname_text(self, shortname):
        """
        """
        p = re.compile("(?:以下简称|下称)[^”，。》、]*?%s" % shortname)
        for text in self.flat_contents:
            text = re.sub("\s", "", text)
            for txt in re.split("[，：。]", text):
                m = p.search(txt)
                if m:
                    return txt
        else:
            return ''
        

class HtReport(Report):
    """
    """
    def __init__(self, html, report_id):
        """后续操作会用到正则，为避免冲突，把原文的半角括号替换成全角括号
        """
        o = "（）"
        i = "()"
        trantab = str.maketrans(i, o)
        html = html.translate(trantab)
        super().__init__(html, report_id)
        
    def _extract_company_bytext(self, regexp):
        is_continue = False
        for text in self.flat_contents:
            if is_continue:
                yield text
                is_continue = False
#            print(text)
            m = re.search(regexp, text)
            if m:
                company = m.group(1)
                if len(company) == 0:
                    is_continue = True
                else:
                    yield company
                    
    def list_company(self):
        companies = set()
        # 1.公告标题里的公司实体
        companies.add(self.company_fullname) 
        # 2.一些子公司
#        companies.update(self.selector.xpath("//text()").re("[子分孙]公司—*?([^—，,。）、；]{5,}?公司)"))
        companies.update(self.selector.xpath("//text()").re("(?:[子分孙]|全资)公司—*?([^—，,。、；]{5,}?公司)"))
        # 3."收到xx公司发出的中标通知书"模式提取
#        for comp in self.selector.xpath("//text()").re("收到(?:\w{,3}方)?(.{4,}?)(?:发出|发来)的"):
        for comp in self.selector.xpath("//text()").re("(?:收到|获|获得|接到)(?:\w{,3}方)?(.{4,}?)(?:发出|发来|出具)的"):
            if len(comp) > 35 and comp.find("和") >= 6:
                companies.add(comp[:comp.find("和")])
                companies.add(comp[comp.find("和")+1:])
            else:
#                print("###", comp)
                companies.add(comp)
        ## 4.xx公司(以下简称“公司”)
        companies.update(self.selector.xpath("//text()").re("(\w+公司)\s*（[^（）]*简称[^（）]+）"))
        # 5.“业主方：xx公司” 模式提取
        pats = ["业主(?:方|名称)?：([^，。；\n]*)",
                "采购人名称：([^，。；\n]*)",
                "采购单位：([^，。；\n]*)",
                "招标(?:人|单位|方)：([^，。；\n]*)",
                "方：([^，。；\n]*)",
                "公司名称：([^，。；\n]*)",
                ]
        for pat in pats:
            comps = list(self._extract_company_bytext(pat))
            companies.update(comps)
        # 6. 与xx签订
        companies.update(self.selector.xpath("//text()").re("与([^，。；\n]+)(:签订|签署)"))
        companies.update(list(self._extract_company_bytext("(?:。|，|^)([^为以此与是、\d]{6,18}公司)")))
        companies.update(list(self._extract_company_bytext("收到与?(.{5,18}公司)")))
        return [ele for ele in companies if bool(ele)]
    
    
